/**
 * 
 */
package org.javahispano.javacup.tacticas_aceptadas.willBender.will.team.players;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;

import org.javahispano.javacup.tacticas_aceptadas.willBender.will.team.ConstantesWill;
import org.javahispano.javacup.tacticas_aceptadas.willBender.will.team.UtilWill;

/**
 * @author William Bernal
 * 
 */
public abstract class WillTeamPlayer {

	private List<Comando> comandosJugador = new ArrayList<Comando>();

	public abstract int getNumeroJugador();

	/**
	 * 1 - Avanzar 2 - Pase Rastrero 3 - Pase Englobado 4 - Pelotazo 5 - Al
	 * Lateral 6 - Patear Al Arco
	 * 
	 * @return
	 */
	public List<Integer> getOrdenJugadorConBalon(SituacionPartido sp) {
		// Si es el arquero
		if (getNumeroJugador() == 0) {
			List<Integer> orden = new LinkedList<Integer>();
			orden.add(4);
			orden.add(3);
			orden.add(2);
			orden.add(1);
			orden.add(6);
			orden.add(5);
			return orden;
		} else {
			if (sp.balon().distancia(Constantes.centroArcoInf) < 33) {
				List<Integer> orden = new LinkedList<Integer>();
				orden.add(4);
				orden.add(5);
				orden.add(3);
				orden.add(2);
				orden.add(1);
				orden.add(6);
				return orden;
			} else if (sp.balon().getY() < -9) {
				List<Integer> orden = new LinkedList<Integer>();
				orden.add(5);
				orden.add(4);
				orden.add(2);
				orden.add(3);
				orden.add(1);
				orden.add(6);
				return orden;
			} else if (sp.balon().getY() < 9) {
				List<Integer> orden = new LinkedList<Integer>();
				orden.add(3);
				orden.add(2);
				orden.add(1);
				orden.add(4);
				orden.add(5);
				orden.add(6);
				return orden;
			} else if (sp.balon().distancia(Constantes.centroArcoSup) > 30) {
				List<Integer> orden = new LinkedList<Integer>();
				orden.add(1);
				orden.add(2);
				orden.add(3);
				orden.add(6);
				orden.add(4);
				orden.add(5);
				return orden;
			} else {
				List<Integer> orden = new LinkedList<Integer>();
				orden.add(6);
				orden.add(1);
				orden.add(2);
				orden.add(3);
				orden.add(4);
				orden.add(5);
				return orden;
			}
		}
	}

	public void getMejorOpcion(SituacionPartido situacionPartido) {
		List<Integer> orden = this.getOrdenJugadorConBalon(situacionPartido);
		if (conBalon(situacionPartido)) {
			if (getNumeroJugador() == 0) {
				int a = 0;
				a++;
			}
			for (Iterator<Integer> iterator = orden.iterator(); iterator
					.hasNext();) {
				Integer opc = iterator.next();
				if (opc == 1) {
					if (this.avanzar(situacionPartido, !iterator.hasNext())) {
						break;
					}
				} else if (opc == 2) {
					if (this
							.paseRastrero(situacionPartido, !iterator.hasNext())) {
						break;
					}
				} else if (opc == 3) {
					if (this.paseEnglobado(situacionPartido, !iterator
							.hasNext())) {
						break;
					}
				} else if (opc == 4) {
					if (this.pelotazo(situacionPartido, !iterator.hasNext())) {
						break;
					}
				} else if (opc == 5) {
					if (this.alLateral(situacionPartido, !iterator.hasNext())) {
						break;
					}
				} else {
					if (this
							.patearAlArco(situacionPartido, !iterator.hasNext())) {
						break;
					}
				}
			}
		}
	}

	private boolean conBalon(SituacionPartido situacionPartido) {
		for (int i = 0; i < situacionPartido.puedenRematar().length; i++) {
			if (situacionPartido.puedenRematar()[i] == getNumeroJugador())
				return true;
		}
		return false;
	}

	public boolean avanzar(SituacionPartido sp, boolean obligacion) {
		if (!UtilWill.rivalInterceptaTrayectoria(
				sp.misJugadores()[getNumeroJugador()], Constantes.penalSup, sp)) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(Constantes.penalSup), 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
			return true;
		}
		Posicion[] pos = sp.misJugadores();
		Posicion[] rivs = sp.rivales();
		if (!obligacion) {
			Posicion miJugador = pos[getNumeroJugador()];
			Posicion rivalMasCercano = rivs[miJugador.indiceMasCercano(rivs)];
			double distanciaRival = miJugador.distancia(rivalMasCercano);
			double posYRival = rivs[pos[getNumeroJugador()].indiceMasCercano(sp
					.rivales())].getY();
			// Si un rival est� a la distancia minima y por delante de quien
			// tiene el balon no se puede avanzar
			if (distanciaRival < ConstantesWill.distanciaMinima
					&& posYRival > pos[getNumeroJugador()].getY()) {
				return false;
			} // de otro modo avanzar
			else if (sp.balon().distancia(
					new Posicion(sp.balon().getX(), sp.balon().getY() + 100)
							.setDentroCampoJuego()) < ConstantesWill.margenCampo) {
				return false;

			} else {
				// Se calcula en que espacio de angulos hay menos jugadores
				// rivales y que me acercan mas al arco rival
				Posicion posAvanza = obtenerDireccionAvanza(
						pos[getNumeroJugador()], rivs, sp);
				if (conBalon(sp)) {
					comandosJugador.add(new ComandoGolpearBalon(
							getNumeroJugador(), posAvanza, 0.45d, 0.45d));
					comandosJugador.add(new ComandoIrA(getNumeroJugador(),
							posAvanza));
				} else {
					comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp
							.balon()));
				}
				return true;
			}
		}
		// Se calcula en que espacio de angulos hay menos jugadores
		// rivales y que me acercan mas al arco rival
		Posicion posAvanza = obtenerDireccionAvanza(pos[getNumeroJugador()],
				rivs, sp);
		if (conBalon(sp)) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					posAvanza, 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), posAvanza));
		} else {
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
		}
		return true;
	}

	// Obtiene la mejor direcci�n para avanzar
	private Posicion obtenerDireccionAvanza(Posicion posicionJugador,
			Posicion[] rivales, SituacionPartido sp) {
		int indiceArqueroRival = UtilWill.getIndiceArqueroRival(sp);
		int conjuntoAngulos = getAngulosPosiblesJugador(posicionJugador);
		double angInf = ConstantesWill.angulosCampoAvanzar[conjuntoAngulos][0];
		double angSup = ConstantesWill.angulosCampoAvanzar[conjuntoAngulos][1];
		double totalAngulos = Math.abs(angInf - angSup);
		double amplitudAngulo = totalAngulos
				/ ConstantesWill.numeroDivisionesAngulos;
		double[][] angulos = new double[ConstantesWill.numeroDivisionesAngulos][3];
		int cont = 0;
		while (cont < ConstantesWill.numeroDivisionesAngulos) {
			angulos[cont][0] = angInf;
			angInf = angInf + amplitudAngulo;
			angulos[cont][1] = angInf;
			angulos[cont][2] = 0;
			cont++;
		}
		for (int i = 0; i < rivales.length; i++) {
			for (int j = 0; j < ConstantesWill.numeroDivisionesAngulos; j++) {
				if (i != indiceArqueroRival
						&& posicionJugador.angulo(rivales[i]) > angulos[j][0]
						&& posicionJugador.angulo(rivales[i]) < angulos[j][1]) {
					angulos[j][2] = angulos[j][2]
							+ (1 / posicionJugador.distancia(rivales[i]));
				}
			}
		}
		for (int i = 0; i < ConstantesWill.numeroDivisionesAngulos; i++) {
			double angIntermedio = angulos[i][0]
					+ (Math.abs(angulos[i][1] - angulos[i][0]) / 2);
			Posicion posMovida = posicionJugador.moverAngulo(angIntermedio,
					ConstantesWill.distanciaAvanzar);
			double distanciaActual = posicionJugador.distancia(new Posicion(
					Constantes.centroArcoSup.getX(), Constantes.centroArcoSup
							.getY() - 4));
			double distanciaNext = posMovida.distancia(new Posicion(
					Constantes.centroArcoSup.getX(), Constantes.centroArcoSup
							.getY() - 4));
			// Con respecto a lo que se acerca a la linea de fondo
			double pesoDistancia = (ConstantesWill.distanciaAvanzar - (Math
					.abs(posMovida.getY() - posicionJugador.getY())))
					/ (ConstantesWill.distanciaAvanzar * 2);
			// Con respecto a lo que se acerca al arco
			double pesoArco = distanciaNext / distanciaActual;

			if (sp.balon().getY() > 20) {
				pesoDistancia = pesoDistancia + (pesoArco * 1.5);
			} else {
				pesoDistancia = pesoDistancia + pesoArco;
			}
			angulos[i][2] = angulos[i][2] + pesoDistancia;
		}
		int anguloResultado = 0;
		double pesoMejorAngulo = 100;
		for (int i = 0; i < ConstantesWill.numeroDivisionesAngulos; i++) {
			if (angulos[i][2] < pesoMejorAngulo) {
				pesoMejorAngulo = angulos[i][2];
				anguloResultado = i;
			}
		}
		double angResult = angulos[anguloResultado][0]
				+ (Math.abs(angulos[anguloResultado][1]
						- angulos[anguloResultado][0]) / 2);
		Posicion posResult = posicionJugador.moverAngulo(angResult,
				ConstantesWill.distanciaAvanzar);
		return posResult;
	}

	private int getAngulosPosiblesJugador(Posicion posicionJugador) {
		if (posicionJugador
				.distancia(new Posicion(-34d, posicionJugador.getY())) < ConstantesWill.margenCampo) {
			if (posicionJugador.distancia(new Posicion(posicionJugador.getX(),
					-52.5d)) < ConstantesWill.margenCampo) {
				// inferior izquierda
				return 0;
			} else if (posicionJugador.distancia(new Posicion(posicionJugador
					.getX(), 52.5d)) < ConstantesWill.margenCampo) {
				// superior izquierda
				return 2;
			} else {
				// centro izquierda
				return 1;
			}
		} else if (posicionJugador.distancia(new Posicion(34d, posicionJugador
				.getY())) < ConstantesWill.margenCampo) {
			if (posicionJugador.distancia(new Posicion(posicionJugador.getX(),
					-52.5d)) < ConstantesWill.margenCampo) {
				// inferior derecha
				return 5;
			} else if (posicionJugador.distancia(new Posicion(posicionJugador
					.getX(), 52.5d)) < ConstantesWill.margenCampo) {
				// superior derecha
				return 3;
			} else {
				// centro derecha
				return 4;
			}
		} else {
			// centro
			return 6;
		}
	}

	public boolean paseRastrero(SituacionPartido sp, boolean obligacion) {
		if (!UtilWill.rivalInterceptaTrayectoria(
				sp.misJugadores()[getNumeroJugador()], Constantes.penalSup, sp)) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(Constantes.penalSup), 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
			return true;
		}
		if (!obligacion) {
			Posicion pos = getMejorPaseRastrero(sp.misJugadores(), sp, sp
					.detalleMisJugadores()[getNumeroJugador()], sp.rivales(),
					getNumeroJugador());
			// Si no encontr� pase posible
			if (pos == null) {
				return false;
			} else {
				JugadorDetalle[] detalles = sp.detalleMisJugadores();
				double distancia = sp.misJugadores()[getNumeroJugador()]
						.distancia(pos);
				double velocidadRemateJugador = (detalles[getNumeroJugador()]
						.getRemate() + 1) / 0.83333334;
				double fuerzaDisparo = (distancia / velocidadRemateJugador)
						/ ConstantesWill.factorDistanciaMaximaPase;
				comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
						pos, fuerzaDisparo + ConstantesWill.deltaFuerzaDisparo,
						false));
			}
		}
		return true;
	}

	private Posicion getMejorPaseRastrero(Posicion[] misJugadores,
			SituacionPartido sp, JugadorDetalle jugadorDetalle,
			Posicion[] rivales, int numeroJugador) {
		double distanciaMaxima = ((jugadorDetalle.getRemate() + 1) / 0.83333334)
				* ConstantesWill.factorDistanciaMaximaPase;
		int[] cercanosMios = misJugadores[numeroJugador]
				.indicesMasCercanos(misJugadores);
		Map<Integer, Double> posibles = new HashMap<Integer, Double>();
		for (int i : cercanosMios) {
			if (i != numeroJugador && i != 0) {
				double dist = misJugadores[numeroJugador]
						.distancia(misJugadores[i]);
				if (dist <= distanciaMaxima) {
					posibles.put(i, dist);
				}
			}
		}
		for (Iterator<Integer> iterator = posibles.keySet().iterator(); iterator
				.hasNext();) {
			Integer indiceCercano = iterator.next();
			if (UtilWill.rivalInterceptaTrayectoria(
					misJugadores[numeroJugador], misJugadores[indiceCercano],
					sp)) {
				iterator.remove();
			}
		}
		if (!posibles.isEmpty()) {
			Double menorPeso = 200d;
			Integer indiceMenor = -1;
			for (Iterator<Integer> iterator = posibles.keySet().iterator(); iterator
					.hasNext();) {
				Integer ind = iterator.next();
				posibles.put(ind, posibles.get(ind)
						+ (misJugadores[ind]
								.distancia(Constantes.centroArcoSup) * 2));
			}
			for (Iterator<Integer> iterator = posibles.keySet().iterator(); iterator
					.hasNext();) {
				Integer ind = iterator.next();
				if (posibles.get(ind) < menorPeso) {
					menorPeso = posibles.get(ind);
					indiceMenor = ind;
				}
			}
			if (indiceMenor == -1)
				return null;
			return misJugadores[indiceMenor];
		}
		return null;
	}

	public boolean paseEnglobado(SituacionPartido sp, boolean obligacion) {
		if (!UtilWill.rivalInterceptaTrayectoria(
				sp.misJugadores()[getNumeroJugador()], Constantes.penalSup, sp)) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(Constantes.penalSup), 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
			return true;
		}
		if (!obligacion) {
			Posicion pos = getMejorPaseRastrero(sp.misJugadores(), sp, sp
					.detalleMisJugadores()[getNumeroJugador()], sp.rivales(),
					getNumeroJugador());
			// Si no encontr� pase posible
			if (pos == null) {
				return false;
			} else {
				JugadorDetalle[] detalles = sp.detalleMisJugadores();
				double distancia = sp.misJugadores()[getNumeroJugador()]
						.distancia(pos);
				double velocidadRemateJugador = (detalles[getNumeroJugador()]
						.getRemate() + 1) / 0.83333334;
				double fuerzaDisparo = (distancia / velocidadRemateJugador)
						/ ConstantesWill.factorDistanciaMaximaPase;
				comandosJugador
						.add(new ComandoGolpearBalon(
								getNumeroJugador(),
								pos,
								fuerzaDisparo
										+ ConstantesWill.deltaFuerzaDisparoGlobo,
								true));
			}
		}
		return true;
	}

	public boolean pelotazo(SituacionPartido sp, boolean obligacion) {
		if (!UtilWill.rivalInterceptaTrayectoria(
				sp.misJugadores()[getNumeroJugador()], Constantes.penalSup, sp)) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(Constantes.penalSup), 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
			return true;
		}
		Posicion[] mios = sp.misJugadores();
		Posicion[] rivs = sp.rivales();
		if (!obligacion) {
			if (sp.balon().getY() < -9) {
				int rivalMasCercano = mios[getNumeroJugador()]
						.indiceMasCercano(rivs);
				if (mios[getNumeroJugador()].distancia(rivs[rivalMasCercano]) < ConstantesWill.distanciaMinima) {
					double anguloRival = mios[getNumeroJugador()]
							.angulo(rivs[rivalMasCercano]);
					if (anguloRival > 0.5235
							&& anguloRival < (Math.PI) - 0.5235) {
						alLateral(sp, obligacion);
					} else {
						comandosJugador.add(new ComandoGolpearBalon(
								getNumeroJugador(), Constantes.penalSup, 1d,
								true));
					}
				} else {
					comandosJugador.add(new ComandoGolpearBalon(
							getNumeroJugador(), Constantes.penalSup, 1d, true));
				}
			} else {
				comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
						Constantes.penalSup, 1d, true));
			}
			return true;
		}
		return false;
	}

	public boolean alLateral(SituacionPartido sp, boolean obligacion) {
		if (!UtilWill.rivalInterceptaTrayectoria(
				sp.misJugadores()[getNumeroJugador()], Constantes.penalSup, sp)) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(Constantes.penalSup), 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
			return true;
		}
		Posicion[] pos = sp.misJugadores();
		if (pos[getNumeroJugador()].getX() < 0) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(-36, pos[getNumeroJugador()].getY()), 1.0d,
					true));
		} else {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(36, pos[getNumeroJugador()].getY()), 1.0d,
					true));
		}
		return true;
	}

	public boolean patearAlArco(SituacionPartido sp, boolean obligacion) {
		if (!UtilWill.rivalInterceptaTrayectoria(
				sp.misJugadores()[getNumeroJugador()], Constantes.penalSup, sp)
				&& sp.balon().distancia(Constantes.penalSup) > 4) {
			comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
					new Posicion(Constantes.penalSup), 0.45d, 0.45d));
			comandosJugador.add(new ComandoIrA(getNumeroJugador(), sp.balon()));
			return true;
		}
		JugadorDetalle[] mios = sp.detalleMisJugadores();
		double presicion = mios[getNumeroJugador()].getPresicion();
		double distanciaDisparo = obtenerDistancia(presicion);
		if (sp.balon().distancia(Constantes.centroArcoSup) > distanciaDisparo
				+ ConstantesWill.deltaDistanciaDisparo) {
			return false;
		} else {
			double anguloVert = obtenerAngulo(sp.balon().distancia(
					Constantes.centroArcoSup));
			int idArqRival = UtilWill.getIndiceArqueroRival(sp);
			Posicion arqRival = sp.rivales()[idArqRival];
			double distPaloDerecho = arqRival
					.distancia(Constantes.posteDerArcoSup);
			double distPaloIzquierdo = arqRival
					.distancia(Constantes.posteIzqArcoSup);
			if (distPaloDerecho > distPaloIzquierdo) {
				comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
						new Posicion(Constantes.posteDerArcoSup.getX() * 0.7,
								Constantes.posteDerArcoSup.getY() + 4), 1,
						anguloVert));
			} else {
				comandosJugador.add(new ComandoGolpearBalon(getNumeroJugador(),
						new Posicion(Constantes.posteIzqArcoSup.getX() * 0.7,
								Constantes.posteIzqArcoSup.getY() + 4), 1,
						anguloVert));
			}
			// System.out.println("Disparo al arco desde: " + sp.balon().getX()
			// + " , " + sp.balon().getY() + " , Dist= "
			// + sp.balon().distancia(Constantes.centroArcoSup));
			return true;
		}
	}

	private double obtenerDistancia(double x) {
		double pendiente = 11;
		double b = 9;
		// Y = mx + b
		double respuesta = (pendiente * x) + b;
		return respuesta;
	}

	private double obtenerAngulo(double x) {
		double pendiente = 1.39;
		double b = -8.54;
		// Y = mx + b
		double respuesta = (pendiente * x) + b;
		return respuesta;
	}

	/**
	 * @return the comandosJugador
	 */
	public List<Comando> getComandosJugador() {
		return comandosJugador;
	}

	/**
	 * @param comandosJugador
	 *            the comandosJugador to set
	 */
	public void setComandosJugador(List<Comando> comandosJugador) {
		this.comandosJugador = comandosJugador;
	}

}